package org.deng.learn.basic.nio.socket;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

/**
 * @author DengLibin
 * @Description nio socket + selector 服务端
 * @Date 2019/2/25 0025 16:32
 */
public class NioServer {

    private Selector selector;//通道选择器

    /**
     * 初始化方法
     * @param port
     * @return
     */
    public NioServer(int port) throws IOException {
        //创建ServerSocket通道
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.socket().bind(new InetSocketAddress(port));
        //非阻塞
        serverSocketChannel.configureBlocking(false);
        //获取通道选择器
        selector = Selector.open();
        //将通道选择器与通道绑定，并为该通道注册SelectionKey.OP_ACCEPT事件，
        //只有当该事件到达时，Selector.select()会返回，否则一直阻塞。
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
    }

    public  void listen() throws IOException {
        System.out.println("服务端启动成功");
        //使用轮询访问selector
        while (!Thread.interrupted()){
            //当有注册的事件到达时，方法返回，否则阻塞 selector.wakeup()可以唤醒
            selector.select();

            //获取selector中的迭代器，选中项为注册的事件
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()){
                SelectionKey key = iterator.next();
                //删除已选的key,防止重复处理
                iterator.remove();
                //客户端请求连接事件
                if(key.isAcceptable()){
                    System.out.println("有请求连接事件");
                    ServerSocketChannel server = (ServerSocketChannel) key.channel();
                    ////获得客户端连接通道
                    SocketChannel channel = server.accept();
                    channel.configureBlocking(false);
                    //向客户端发消息
                    channel.write(ByteBuffer.wrap(new String("send message to client").getBytes()));
                    //在与客户端连接成功后，为客户端通道注册SelectionKey.OP_READ事件。
                    channel.register(selector, SelectionKey.OP_READ);
                }else if(key.isReadable()){
                    System.out.println("有可读数据事件");
                    //获取客户端传输数据可读取消息通道。
                    SocketChannel channel = (SocketChannel)key.channel();
                    //创建读取数据缓冲器
                    ByteBuffer buffer = ByteBuffer.allocate(1024);
                    int read = channel.read(buffer);
                    if(read <= 0){
                        System.out.println("client disconnected");
                        key.cancel();
                        continue;
                    }
                    byte[] data = buffer.array();
                    String message = new String(data,0, buffer.position());
                    System.out.println("receive message from client, size:" + buffer.position() + " msg: " + message);
                }
            }
        }
    }

    public  static  void main(String[] args) throws IOException {
        NioServer nioServer = new NioServer(10250);
        nioServer.listen();
    }
}
